#! /usr/bin/env python
# coding=utf-8
# ******************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Author: senlin
# Create: 2021-06-20
# ******************************************************************************/


"""
This is a simple script to query that contact person for specific package
"""

import os
import re
import yaml
import stat

from genericpath import exists, isfile
from src.libs.logger import logger
from src.config import global_config
from src.config import constant
from src.libs.base import http
from src.libs.csvrw import CSVRW
from src.libs.executecmd import ExecuteCmd
from src.api.gitee import GiteeAPI


class OpenEulerCommunityRepoInfoQuery(object):
    """
    get sig maintainer and contributor of package
    """
    
    R_FLAG = os.O_RDONLY
    R_MODES = stat.S_IRUSR
    W_FLAG = os.O_WRONLY | os.O_CREAT
    W_MODES = stat.S_IWUSR | stat.S_IRUSR
    
    def __init__(self):
        """
        @description :
        -----------
        @param :
        -----------
        @returns :
        -----------
        """

        self.signames = []
        self.sig_repo_map = dict()
        self.get_local_sigs_yaml() # 读取本地的sigs.yaml内容
        self.exps = dict()
        self.get_local_excep_repo_name()
        self.openeuler_repo_owner = dict()
        self.load_openEuler_owmers_csv()
        self._cache_data = self.load_cache_data()
        
    def load_cache_data(self):
        """
        
        Args:
        
        Returns:
        
        """
        
        cache_data = dict()
        if not os.path.exists(global_config.GITEE_OE_FULL_REPO_SIGS):
            logger.warning(f"local {global_config.GITEE_OE_FULL_REPO_SIGS} not exist")
            return cache_data
        with os.fdopen(os.open(global_config.GITEE_OE_FULL_REPO_SIGS, self.R_FLAG, self.R_MODES), "r") as fout:
            cache_data = yaml.safe_load(fout)
                
        return cache_data
    
    def load_openEuler_owmers_csv(self):
        """
        get owner of openeuler packages
        Args:
        
        Returns:
        
        """
        
        self.openeuler_repo_owner, __ = CSVRW.read_2_dict(global_config.LOCAL_OPENEULER_OWNERS, encoding='gbk')

        
    def get_package_details(self, package):
        """
        
        Args:
        
        Returns:
            package_owner_info:{
                'sig':"",
            }
        """


        # Package,Sig,Maintainer,Maintainer_email,QA,Team,Owner,Owner_email
        package_owner_info = dict()
        package_owner_info['sig'] = self._cache_data.get(package, {}).get('sig', '')
        package_owner_info['assignee'] =  self._cache_data.get(package, {}).get('assignee', '')

        if not package_owner_info:
            # oe_members_file = open(global_config.GITEE_OE_MEMBERS_ID_YAML, encoding='utf-8')
            # oe_members_data = yaml.safe_load(oe_members_file)
            package_owner_info = dict()
            package_owner_info['sig'] = self.query_sig_of_repo(package)
            package_owner_info['assignee'] = 'NA'
            # package_owner_info['qa'] = 'NA'
            # maintainer_ids, maintainer_emails = self.query_maintainer_info(package_owner_info['Sig'], oe_members_data)
            # package_owner_info['Maintainer'] = maintainer_ids
            # package_owner_info['Maintainer_email'] = ';'.join(maintainer_emails)
            # oe_members_file.close()

        return package_owner_info

    def analyse_and_upload_notify_emails(self, sig_names):
        """
        
        Args:
        
        Returns:
        
        """
        
        notify_emails = {
            "community": [],
            "hw": []
        }
        community_emails = {'test'}
        # community_emails = list()

        oe_members_file = open(global_config.GITEE_OE_MEMBERS_ID_YAML, encoding='utf-8')
        oe_members_data = yaml.safe_load(oe_members_file)
        for sig_name in sig_names:
            __, maintainer_emails = self.query_maintainer_info(sig_name, oe_members_data)
            community_emails = community_emails.union(set(maintainer_emails))
        oe_members_file.close()
        
        notify_emails['community'] = list(community_emails)
        logger.info(notify_emails['community'])
        save_str = ';'.join(notify_emails['community'])
        with open('notify_emails.txt', "w") as notify_f:
            notify_f.writelines(save_str)

        return notify_emails

    def get_local_excep_repo_name(self):
        """
        get the repo_name:srpm_name spec_name dict
        Args:
        
        Returns:
        
        """
        with open(global_config.LOCAL_REPO_EXCEP, encoding='utf-8') as f:
            self.exps = yaml.safe_load(f)
        
    
    def get_local_sigs_yaml(self):
        """
        get the content of local sigs.yaml 

        returns:
            sigs: content of sigs.yaml
                exp:
                A-Tune:['python-nni', 'python-astor']
        """
        sigs = dict()
        logger.info(os.getcwd())
        sigs_dir = os.listdir(global_config.LOCAL_SIGS)
        for sig_dir in sigs_dir:
            if sig_dir == 'TC' or isfile(os.path.join(global_config.LOCAL_SIGS, sig_dir)):
                continue

            self.signames.append(sig_dir)
            sigs[sig_dir] = []

            sig_repos_dir = os.path.join(global_config.LOCAL_SIGS, sig_dir, 'src-openeuler') 
            try:
                src_oe_pkgs_dir = os.listdir(sig_repos_dir)
            except FileNotFoundError:
                logger.warning(f"{sig_dir} has no src-openeuler/repo")
                continue

            for src_oe_prepos in src_oe_pkgs_dir:
                repos_yamls = os.listdir(os.path.join(sig_repos_dir, src_oe_prepos))
                repo_names = [repo_yaml.replace('.yaml', '') for repo_yaml in repos_yamls]
                sigs[sig_dir].extend(repo_names)

        self.sig_repo_map = sigs

    
    def get_gitee_repo_related_name(self, name_type, name):
        """
        get the real package repo name on gitee

        args:
            name_type: the name of the package/srpm/spec
            name: the value of name
        returns:
            related names: packsge, srpm, spec
        """

        spec_name = name
        gitee_repo_name = name
        srpm_name = name
        for key, value in self.exps.items():
            if name_type == 'spec':
                if value.get("spec") == name:
                    gitee_repo_name = key
                    srpm_name = value.get("srpm")
                    break
            if name_type == 'srpm':
                if value.get("srpm") == name:
                    gitee_repo_name = key
                    spec_name = value.get("spec")
                    break
            if name_type == 'gitee_repo':
                if key == name:
                    spec_name = value.get("spec")
                    srpm_name = value.get("srpm")
                    break
        return gitee_repo_name, spec_name, srpm_name


    def query_sig_of_repo(self, package):
        """
        get the sig name of specific package
        args:
            pacakge
        returns:
            sig_name: SIG name to which package belongs
        """ 
        sig_name = "NA"
        if not self.sig_repo_map:
            return sig_name
        for sig in self.signames:
            for repo in self.sig_repo_map[sig]:
                if repo == package:
                    return sig
        return sig_name

    @staticmethod
    def query_maintainer_info(sig_name, oe_members_data):
        """
        get maintainers of specific sig

        args:
            sig_name: name of sig
        returns:
            maintainers: maintainers of sig
        """

        maintainer_ids = []
        maintainer_emails = []

        if sig_name == 'NA':
            logger.info("invalid sig_name")
            return maintainer_ids, maintainer_emails

        sig_maintainer_yaml = global_config.LOCAL_SIGS_INFO_YAML.format(signame = sig_name)

        # 优先查询sig-info.yaml
        if os.path.exists(sig_maintainer_yaml):
            with open(sig_maintainer_yaml, encoding='utf-8') as f:
                yaml_content = yaml.safe_load(f)
                maintainer_content = yaml_content.get('maintainers', {})
                if not maintainer_content:
                    return maintainer_ids, maintainer_emails

                for maintainer_info in maintainer_content:
                    if len(maintainer_ids) == 2:
                        break
                    maintainer_id = maintainer_info.get('gitee_id', None)
                    maintainer_email = maintainer_info.get('email', 'NA')
                    
                    if maintainer_email == 'NA':
                        maintainer = oe_members_data.get(maintainer_id, None)
                        if maintainer:
                            maintainer_email = maintainer['email']
                        
                    maintainer_ids.append(maintainer_id)
                    if maintainer_email != 'NA':
                        maintainer_emails.append(maintainer_email)
                    
        else: # 尝试解析OWNERS
            # logger.info("try to analyse OWNERS: %s", sig_name)
            sig_OWNERS = global_config.LOCAL_SIGS_OWNERS.format(signame = sig_name)
            if os.path.exists(sig_OWNERS):
                with open(sig_OWNERS, encoding='utf-8') as f:
                    yaml_content = yaml.safe_load(f)
                    maintainer_content = yaml_content.get('maintainers', {})
                    if not maintainer_content:
                        return maintainer_ids, maintainer_emails

                    for gitee_id in maintainer_content:
                        if len(maintainer_ids) == 2:
                            break
                        
                        maintainer_id = gitee_id
                        maintainer_email = 'NA'
                        maintainer = oe_members_data.get(gitee_id, None)
                        if maintainer:
                            maintainer_email = maintainer['email']
                            
                        maintainer_ids.append(maintainer_id)
                        if maintainer_email != 'NA':
                            maintainer_emails.append(maintainer_email)

        # maintainer_emails = ';'.join(maintainer_emails)
        # logger.info("find maintainer info for %s: %s---%s", sig_name, maintainer_ids, maintainer_emails)
        return maintainer_ids, maintainer_emails

    @staticmethod
    def get_spec_file_names(package_name, branch='master'):
        """
        get spec file names of package in src-openeuler
        Args:
            package_name:
            branch:
        Returns:
            spec_files
        """

        spec_files = []
        gitee_api_handle = GiteeAPI(version='v5', enterprise='src-openeuler')
        tree_files = gitee_api_handle.get_repo_tree(package_name, branch)

        for file in tree_files:
            if '.spec' in file:
                spec_files.append(file)

        logger.info("find spec files:%s for %s:" % (spec_files, package_name))
        return spec_files

    @staticmethod
    def get_spec_context(package_name, spec_name, branch = 'master'):
        """
        get spec file content of package
        args:
            package: package name/spec filename
        returns:
            spec_content: content of spec file
        """

        specurl = constant.SPEC_URL.format( 
            package = package_name, 
            branch = branch, 
            specfile = spec_name)
        logger.info(f"specurl:{specurl}")
        resp = http.get(specurl)

        if resp is None or resp.status_code != 200:
            logger.warning(f"get spec: None")
            return None
        spec_str = resp.text
        if spec_str is None:
            logger.error("spec_str is None")
            return None
        else:
            spec = spec_str.splitlines()
        return spec

    def get_latest_contributors(self, package_name, branch, max_eamil_num=1):
        """
        get latest contributors's emails

        args:
            package: package name/spec filename
            max_eamil_num: limit the maximum number of eamils
        returns:
            emails: eamils of latest contributors
        """

        latest_contributors = []
        spec_file_names = self.get_spec_file_names(package_name, branch)
        for spec_name in spec_file_names:
            spec = OpenEulerCommunityRepoInfoQuery.get_spec_context(package_name, spec_name, branch)
            if spec is None:
                logger.error("get spec of %s failed!", package_name)
                return 'NA'

            emails = self.get_emails_of_contributors(spec)
            if emails:
                latest_contributors.append(emails[0])
            
        return latest_contributors

    def get_spec_version(self, package_name, branch='master'):
        """
        get init contributors's emails

        args:
            package: package name/spec filename
        returns:
            emails: 
        """

        init_contributors = []
        spec_file_names = self.get_spec_file_names(package_name, branch)
        for spec_name in spec_file_names:
            spec = OpenEulerCommunityRepoInfoQuery.get_spec_context(package_name, spec_name, branch)
            if spec is None:
                logger.error("get spec of %s failed!", package_name)
                return 'NA'

            ver = ''
            rel = ''
            for line in spec:
                if line.startswith("Version:"):
                    ver = line.split("Version:")
                elif line.startswith("version:"):
                    ver = line.split("version:")

                # if line.startswith("release:"):

            
        return init_contributors

    def get_init_contributors(self, package_name, branch='master'):
        """
        get init contributors's emails

        args:
            package: package name/spec filename
        returns:
            emails: 
        """

        init_contributors = []
        spec_file_names = self.get_spec_file_names(package_name, branch)
        for spec_name in spec_file_names:
            spec = OpenEulerCommunityRepoInfoQuery.get_spec_context(package_name, spec_name, branch)
            if spec is None:
                logger.error("get spec of %s failed!", package_name)
                return 'NA'

            emails = self.get_emails_of_contributors(spec)
            if emails:
                init_contributors.append(emails[-1]) 
            
        return init_contributors
        
    @staticmethod
    def get_emails_of_contributors(spec, max_eamil_num=1):
        """
        analyse the email of contributor in changelog
        args:
            spec: content of spec file
            max_eamil_num: limit the maximum number of eamils
        returns:
            emails: eamils of latest max_eamil_num contributors
        """
        emails = []
        num = 0
        in_changelog = False
        for line in spec:
            if line.startswith("%changelog"):
                in_changelog = True
            if in_changelog and line.startswith("*") and num < max_eamil_num:
                try:
                    regular = re.compile(r'[0-9a-zA-Z\.\-]+@[0-9a-zA-Z\.\-]+[com, org, net]')
                    email = re.findall(regular, line)[0]
                    logger.info(f"email: {email}")
                    emails.append(email)
                    num = num + 1
                except IndexError:
                    logger.error(f"analyse developer for {line} failed")
                    emails.append(line)
        return emails

    def query_sig_repo(self, sig_name):
        """
        query the repo name list of sig_name
        Args:
            sig_name: name of sig
        Returns:
            sig_repos: list of repo of sig
        """
        
        sig_repos = []
        
        try:
            sig_repos = self.sig_repo_map[sig_name]
        except KeyError as ke:
            logger.error(ke)
        return sig_repos

    def query_full_sig_repos(self):
        """
        基于gitee.com/src-openeuler和 openEuler/community生成软件仓的基础信息表
        Args:
        
        Returns:
        
        """

        src_pkg_data = dict()
        
        # 获取最新openEuler的所有repo及id
        gitee_handle = GiteeAPI(version='v8', enterprise='openEuler')
        projects = gitee_handle.get_enterprise_projects_id() or dict()
        
        # 获取最新openEuler的所有member及id
        members = gitee_handle.get_enterprise_member_ids_v8()
        
        # 获取最新openEuler的所有repo及负责人name
        assignee_data, _ = CSVRW.read_2_dict(global_config.LOCAL_OPENEULER_OWNERS)
        if not assignee_data:
            logger.warning("Invalid input %s file", global_config.LOCAL_OPENEULER_OWNERS)
            return False
        
        for c_sig_name in self.signames:
            sig_repos = self.query_sig_repo(c_sig_name)
            for repo_name in sig_repos:
                src_pkg_data[repo_name] = dict()
                src_pkg_data[repo_name]['sig'] = c_sig_name
                src_pkg_data[repo_name]['repo_id'] = projects.get(repo_name, {}).get('id', '')
                src_pkg_data[repo_name]['assignee'] = assignee_data.get(repo_name, {}).get('assignee', '')
                src_pkg_data[repo_name]['assignee_id'] = members.get(src_pkg_data[repo_name]['assignee'], {}).get('id', '')

                logger.info("refresh [%s]'s info: [%s]", repo_name, src_pkg_data[repo_name])
        return src_pkg_data
    
    def query_full_sig_repos_bak(self):
        """
        基于gitee.com/src-openeuler和 openEuler/community生成软件仓的基础信息表
        Args:
        
        Returns:
        
        """

        src_pkg_data = dict()
        # 如果需要加入sigmaintainer信息，再打开
        oe_members_file = open(global_config.GITEE_OE_MEMBERS_ID_YAML, encoding='utf-8')
        oe_members_data = yaml.safe_load(oe_members_file)

        for c_sig_name in self.signames:
            sig_repos = self.query_sig_repo(c_sig_name)
            maintainer_ids, maintainer_emails = self.query_maintainer_info(c_sig_name, oe_members_data)
            for repo_name in sig_repos:
                # logger.info(f"{repo_name}")
                src_pkg_data[repo_name] = dict()
                src_pkg_data[repo_name]['name'] = repo_name
                src_pkg_data[repo_name]['sig'] = c_sig_name
                src_pkg_data[repo_name]['maintainers'] = maintainer_ids
                src_pkg_data[repo_name]['maintainer_emails'] = ';'.join(maintainer_emails)
                src_pkg_data[repo_name]['init_contributor_emails'] = self.get_init_contributors(repo_name)
                src_pkg_data[repo_name]['init_contributor_emails'] = 'NA'
        oe_members_file.close()

        return src_pkg_data

    def get_related_email(self, repo_name, branch='master'):
        """
        @description : 获取软件包的sig组信息、最近的贡献者邮箱信息
        -----------
        @param :
        -----------
        @returns :
        -----------
        """

        sig_name = self.query_sig_of_repo(repo_name)
        oe_members_file = open(global_config.GITEE_OE_MEMBERS_ID_YAML, encoding='utf-8')
        oe_members_data = yaml.safe_load(oe_members_file)
        maintainer_ids, maintainer_emails = self.query_maintainer_info(sig_name, oe_members_data)
        developer_email = self.get_latest_contributors(repo_name, branch)
        oe_members_file.close()
        return sig_name, maintainer_ids, maintainer_emails, developer_email


# sig_info_query = OpenEulerCommunityRepoInfoQuery()